#pragma once
#include "Precompile.h"

namespace Utility{
	namespace Math{
		enum VectorPos 	{ 	
							_x_ = 0, 
							_y_, 
							_z_, 
							_w_ 
						};

		template<typename InternalType = float, unsigned int VectorSize = 3>
		class Vector{
		public:
			Vector(){
				for(int i=0; i<VectorSize; i++){
					a[i] = InternalType(0.0);
				}
			}

			Vector(InternalType *data){
				std::copy(data,data+VectorSize,a);
			}

			~Vector(){}

			InternalType a[VectorSize];

			InternalType length(){
				InternalType value = a[0]*a[0];
				for(int i=1; i<VectorSize; i++){
					value += a[i]*a[i];
				}
				return sqrt(value);
			}

			void normalize(){
				InternalType len = length();
				for(int i=0; i<VectorSize; i++){
					a[i] /= len;
				}
			}

			/// Overloading
			InternalType& operator() (VectorPos pos){
				return a[pos];
			}

			void operator() (VectorPos pos, InternalType value){
				a[pos] = value;
			}

			// Multiplication overloads
			Vector<InternalType,VectorSize> operator* (Vector<InternalType,VectorSize> &other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i]*other(VectorPos(i)));
				}

				return result;
			}

			Vector<InternalType,VectorSize> operator* (InternalType other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i]*other);
				}

				return result;
			}

			void operator*= (Vector<InternalType,VectorSize> &other){
				for(int i=0; i<VectorSize; i++){
					a[i] *= other(i);
				}
			}

			void operator*= (InternalType type){
				for(int i=0; i<VectorSize; i++){
					a[i] *= type;
				}
			}

			InternalType dot(Vector &vec){
				if(VectorSize == 2)
					return (a[_x_]*vec(_x_)+a[_y_]*vec(_y_));
				if(VectorSize == 3)
					return (a[_x_]*vec(_x_)+a[_y_]*vec(_y_)+a[_z_]*vec(_z_));
				else if(VectorSize == 4)
					return (a[_x_]*vec(_x_)+a[_y_]*vec(_y_)+a[_z_]*vec(_z_)+a[_w_]*vec(_w_));
				else
					return 0;
			}

			// Addition overloads
			Vector<InternalType,VectorSize> operator+ (Vector<InternalType,VectorSize> &other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i]+other(VectorPos(i)));
				}

				return result;
			}

			Vector<InternalType,VectorSize> operator+ (InternalType other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i]+other);
				}

				return result;
			}

			void operator+= (Vector<InternalType,VectorSize> &other){
				for(int i=0; i<VectorSize; i++){
					a[i] += other(VectorPos(i));
				}
			}

			// Subtraction overloads
			Vector<InternalType,VectorSize> operator- (Vector<InternalType,VectorSize> &other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i]-other(VectorPos(i)));
				}

				return result;
			}

			Vector<InternalType,VectorSize> operator- (InternalType other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i]-other);
				}

				return result;
			}

			Vector<InternalType,VectorSize> operator- (){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),-a[i]);
				}

				return result;
			}

			void operator-= (Vector<InternalType,VectorSize> &other){
				for(int i=0; i<VectorSize; i++){
					a[i] -= other(VectorPos(i));
				}
			}

			// Division overloads
			void operator/= (Vector<InternalType,VectorSize> &other){
				for(int i=0; i<VectorSize; i++){
					a[i] /= other(VectorPos(i));
				}
			}

			void operator/= (InternalType other){
				for(int i=0; i<VectorSize; i++){
					a[i] /= other;
				}
			}

			Vector<InternalType,VectorSize> operator/ (Vector<InternalType,VectorSize> &other){
				Vector<InternalType,VectorSize> result;

				for(int i=0; i<VectorSize; i++){
					result(VectorPos(i),a[i] / other(VectorPos(i)));
				}

				return result;
			}

			// Assignement overload
			void operator= (Vector<InternalType,VectorSize> &other){
				for(int i=0; i<VectorSize; i++){
					a[i] = other(VectorPos(i));
				}
			}

			// Equal overload
			bool operator== (Vector<InternalType,VectorSize> &other){
				InternalType diff = 0.0;
				for(int i=0; i<VectorSize; i++){
					diff += abs(a[i]-other(VectorPos(i)));
				}

				if(diff < 0.01 && diff > -0.01)
					return true;
				return false;
			}
		};
	}
}